#include "./pick_service.h"
#include "../../task/heat/heat_task.h"
#include "../../driver/heat/parts.h"
#include "../../driver/heat/ntc_sensor.h"
#include "../../utils/progress.h"
#include "../../driver/heat/motors.h"
#include "../../driver/heat/sensor.h"
#include "../../utils/alg.h"

namespace service {

using namespace task;
using namespace utils;
using namespace driver;

// 每分钟300ml 60 * 1000 => 300ml
// 60 * 1000 / 300 = 200
Error draw_water(uint8_t ml) {
    Error err;

    parts->water_pump_open();
    parts->water_valve_open();

    osDelay(ml * 200);

    parts->water_pump_close();
    parts->water_valve_close();

    return err;
}

Error draw_water_with_flow(uint8_t ml) {
    Error err;
    Progress timeout_prog;
    Progress prog;
    uint16_t last_flow = 0;

    sensor->flow_reset();
    parts->water_pump_open();
    parts->water_valve_open();

    while ( true ) {

        uint16_t flow = sensor->flow();
        if (flow > ml) {
            break;
        }
        
        if (prog.is_after(5000)) {
            prog.reset();
            if (alg::abs(last_flow, flow) <= 5) {
                err = ec::HEAT_DRAW_WATER_INVALID;
                break;
            }
            last_flow = flow;
        }

        if (timeout_prog.is_after(60 * 1000)) {
            err = ec::HEAT_DRAW_WATER_TIMEOUT;
            break;
        }

        osDelay(200);
    }
    parts->water_pump_close();
    parts->water_valve_close();
finish:
    return err;
}

static Error wait_water_temp(uint8_t temp, uint32_t timeout = 60 * 1000) {
    Progress prog;
    Error err;
    while (true) {
        if (ntcSensor->water_temp() >= temp) {
            break;
        }

        if (!ntcSensor->is_water_ok()) {
            err = ec::HEAT_WATER_SENSOR_FAIL;
            goto finish;
        }

        if (!heatTask->water_valid(temp)) {
            err = ec::HEAT_WATER_HEAT_INVALID;
            goto finish;
        }

        if (prog.is_after(timeout)) {
            err = ec::HEAT_WATER_TIMEOUT;
            goto finish;
        }
        osDelay(1000);
    }
finish:
    return err;
}

static Error wait_steam_temp(uint8_t temp, uint32_t timeout = 60 * 1000) {
    Error err;
    Progress prog;
    while (true) {
        if (ntcSensor->steam_temp() >= temp) {
            break;
        }

        if (!ntcSensor->is_steam_ok()) {
            err = ec::HEAT_STEAM_SENSOR_FAIL;
            goto finish;
        }

        if (!heatTask->steam_valid(temp)) {
            err = ec::HEAT_STEAM_HEAT_INVALID;
            goto finish;
        }

        if (prog.is_after(timeout)) {
            err = ec::HEAT_STEAM_TIMEOUT;
            goto finish;
        }
        osDelay(1000);
    }
finish:
    return err;
}

// 使用夹管阀放水
static Error drop_valve_water(uint8_t sec) {
    Error err = motors->water_right();
    if (err) {
        goto finish;
    }
    for (uint8_t i = 0; i < sec; i ++) {
        parts->drop_valve_open();
        osDelay(500);
        parts->drop_valve_close();
        osDelay(2000);
    }
finish:
    return err;
}

Error stir_powder(uint8_t ml, uint8_t water_temp) {

    Error err = motors->stir_box_close();
    if (err) {
        goto finish;
    }
    err = wait_water_temp(water_temp);
    if (err) {
        goto finish;
    }
 
    err = draw_water(ml / 4);
    if (err) {
        goto finish;
    }
    parts->stir_motor_open();
    err = draw_water(ml / 4 * 3);
    if (err) {
        goto finish;
    }
    osDelay(20 * 1000);
finish:
    parts->stir_motor_close();
    return err;
}

Error drop_steam(uint8_t sec, uint8_t steam_temp) {
    Error err = wait_steam_temp(steam_temp);
    if (err) {
        goto finish;
    }
    parts->steam_pump_open();
    parts->steam_valve_open();
    osDelay(sec * 1000);
    parts->steam_pump_close();
    osDelay(10 * 1000);
    parts->steam_valve_close();
finish:
    return err;
} 

Error clean(uint8_t sec) {
    Error err = motors->stir_box_close();
    if (err) {
        goto finish;
    }
    parts->stir_motor_open();
    parts->clean_valve_open();
    parts->clean_pump_open();
    osDelay(sec * 1000);
    parts->clean_pump_close();
    osDelay(10 * 1000);
    parts->clean_valve_close();
    parts->stir_motor_close();
    err = drop_valve_water(sec);
    if (err) {
        goto finish;
    }
finish:
    return err;
}

Error pre_water(uint8_t ml, uint8_t water_temp) {
    Error err = motors->stir_box_close();
    if (err) {
        goto finish;
    }
    err = wait_water_temp(water_temp);
    if (err) {
        goto finish;
    }
    err = draw_water(ml);
    if (err) {
        goto finish;
    }
    osDelay(10 * 1000);
    err = motors->stir_box_open();
    if (err) {
        goto finish;
    }
finish:
    return err;
}

}
